package Utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.digest.DigestUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import Beans.Agent;

public class AlipayHelper {

	// 合作身份者ID，以2088开头由16位纯数字组成的字符串
	public static String partner = "2088011454654154";
	// 商户的私钥
	public static String key = "8wtfjauyt8pi17ywpfky6vrxcts67fpw";
	// 字符编码格式 目前支持 gbk 或 utf-8
	public static String input_charset = "utf-8";
	// 签名方式 不需修改
	public static String sign_type = "MD5";

	// 支付类型
	public static String payment_type = "1";
	// 必填，不能修改
	// 服务器异步通知页面路径
	public static String notify_url = "";
	// 需http://格式的完整路径，不能加?id=123这类自定义参数

	// 页面跳转同步通知页面路径
//	public static String return_url = "http://112.124.59.126:8080/YouBoxAdminTest/returnPay";
	public static String return_url = "http://112.124.59.126:8080/YouBoxAdmin/returnPay";
	// 需http://格式的完整路径，不能加?id=123这类自定义参数，不能写成http://localhost/

	// 卖家支付宝帐户
	public static String seller_email = "pay@24box.cn";
	// 必填

	// 商户订单号
	public static String out_trade_no = null;
	// 商户网站订单系统中唯一订单号，必填

	// 订单名称
	public static String subject = "快递账户充值";
	// 必填

	// 付款金额
	public static String total_fee = null;
	// 必填

	// 订单描述
	public static String body = null;
	// 商品展示地址
	public static String show_url = "http://service.24box.cn/";
	// 需以http://开头的完整路径，例如：http://www.xxx.com/myorder.html

	// 防钓鱼时间戳
	public static String anti_phishing_key = null;
	// 若要使用请调用类文件submit中的query_timestamp函数

	// 客户端的IP地址
	public static String exter_invoke_ip = "222.76.126.53";
	// 非局域网的外网IP地址，如：221.0.0.1

	/**
	 * 支付宝消息验证地址
	 */
	private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";

	/**
	 * 支付宝提供给商户的服务接入网关URL(新)
	 */
	private static final String ALIPAY_GATEWAY_NEW = "https://mapi.alipay.com/gateway.do?";
	
	/**
	 * 初始化参数
	 * @param agent
	 */
	public static void initParam(Agent agent){
		if(agent.getRecharge()==0){
			partner = "2088011454654154";
			key = "8wtfjauyt8pi17ywpfky6vrxcts67fpw";
			seller_email = "pay@24box.cn";
			show_url = "http://service.24box.cn/";
			exter_invoke_ip = "222.76.126.53";
		}else{
			partner = agent.getPartner();
			key = agent.getAgentKey();
			seller_email = agent.getSellerEmail();
			show_url = agent.getShowUrl();
			exter_invoke_ip = agent.getExterInvokeIp();
		}
	}

	/**
	 * 构造URL
	 * 
	 * @param serialNum
	 * @param phoneNum
	 * @param expressName
	 * @param money
	 * @return
	 * @throws Exception
	 * @throws DocumentException
	 * @throws IOException
	 */
	public static String buildURL(Agent agent,String serialNum, BigInteger phoneNum,
			String expressName, BigDecimal money) throws Exception,
			DocumentException, IOException {
		initParam(agent);
		StringBuffer url = new StringBuffer();
		Map<String, String> sParaTemp = new HashMap<String, String>();
		sParaTemp.put("service", "create_direct_pay_by_user");
		sParaTemp.put("partner", partner);
		sParaTemp.put("_input_charset", input_charset);
		sParaTemp.put("payment_type", payment_type);
		sParaTemp.put("notify_url", notify_url);
		sParaTemp.put("return_url", return_url);
		sParaTemp.put("seller_email", seller_email);
		sParaTemp.put("out_trade_no", serialNum);
		sParaTemp.put("subject", subject);
		// sParaTemp.put("subject", new String(subject.getBytes("ISO-8859-1"),
		// "UTF-8"));
		// sParaTemp.put("subject", new String(URLEncoder.encode(subject,
		// "UTF-8").getBytes(), "ISO-8859-1"));
		sParaTemp.put("total_fee", money.toString());
		sParaTemp.put("body", phoneNum + expressName + "充值" + money + "元。");
		// sParaTemp.put("body",new String((phoneNum + expressName + "充值" +
		// money+ "元。").getBytes("ISO-8859-1"), "UTF-8"));
		// sParaTemp.put("body",new String(URLEncoder.encode(phoneNum +
		// expressName + "充值" + money+ "元。", "UTF-8").getBytes(),
		// "ISO-8859-1"));
		sParaTemp.put("show_url", show_url);
		sParaTemp.put("anti_phishing_key", query_timestamp());
		sParaTemp.put("exter_invoke_ip", exter_invoke_ip);
		Map<String, String> sPara = buildRequestPara(sParaTemp);
		url.append(ALIPAY_GATEWAY_NEW);
		url.append("_input_charset=" + input_charset);
		List<String> keys = new ArrayList<String>(sPara.keySet());
		for (int i = 0; i < keys.size(); i++) {
			String name = (String) keys.get(i);
			String value = (String) sPara.get(name);
			if ("subject".equals(name) || "body".equals(name)) {
				value = new String(
						URLEncoder.encode(value, "UTF-8").getBytes(),
						"ISO-8859-1");
			}
			url.append("&" + name + "=" + value);
		}
		/*
		 * url.append("&service=create_direct_pay_by_user");
		 * url.append("&partner="+partner);
		 * url.append("&payment_type="+payment_type); //
		 * url.append("&notify_url="+notify_url);
		 * url.append("&return_url="+return_url);
		 * url.append("&seller_email="+seller_email);
		 * url.append("&out_trade_no="+serialNum);
		 * url.append("&subject="+subject); url.append("&total_fee="+total_fee);
		 * url.append("&body="+phoneNum + expressName + "充值" + total_fee+ "元。");
		 * url.append("&show_url="+show_url);
		 * url.append("&anti_phishing_key="+query_timestamp());
		 * url.append("&exter_invoke_ip="+exter_invoke_ip);
		 */
		return url.toString();
	}

	/**
	 * 返回流水号
	 * 
	 * @return
	 */
	public static String getSerialNum(String webName) {
		StringBuffer buffer = new StringBuffer();
		buffer.append(webName.toUpperCase());
		buffer.append(new Date().getTime());
		buffer.append("ZD");
		return buffer.toString();
	}

	/**
	 * 签名字符串
	 * 
	 * @param text
	 *            需要签名的字符串
	 * @param sign
	 *            签名结果
	 * @param key
	 *            密钥
	 * @param input_charset
	 *            编码格式
	 * @return 签名结果
	 */
	public static boolean verify(String text, String sign, String key,
			String input_charset) {
		text = text + key;
		String mysign = DigestUtils
				.md5Hex(getContentBytes(text, input_charset));
		if (mysign.equals(sign)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 根据反馈回来的信息，生成签名结果
	 * 
	 * @param Params
	 *            通知返回来的参数数组
	 * @param sign
	 *            比对的签名结果
	 * @return 生成的签名结果
	 */
	private static boolean getSignVeryfy(Map<String, String> Params, String sign) {
		// 过滤空值、sign与sign_type参数
		Map<String, String> sParaNew = paraFilter(Params);
		// 获取待签名字符串
		String preSignStr = createLinkString(sParaNew);
		// 获得签名验证结果
		boolean isSign = false;
		if (sign_type.equals("MD5")) {
			isSign = verify(preSignStr, sign, key, input_charset);
		}
		return isSign;
	}

	/**
	 * 获取远程服务器ATN结果
	 * 
	 * @param urlvalue
	 *            指定URL路径地址
	 * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true
	 *         返回正确信息 false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
	 */
	private static String checkUrl(String urlvalue) {
		String inputLine = "";

		try {
			URL url = new URL(urlvalue);
			HttpURLConnection urlConnection = (HttpURLConnection) url
					.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(
					urlConnection.getInputStream()));
			inputLine = in.readLine().toString();
		} catch (Exception e) {
			e.printStackTrace();
			inputLine = "";
		}

		return inputLine;
	}

	/**
	 * 获取远程服务器ATN结果,验证返回URL
	 * 
	 * @param notify_id
	 *            通知校验ID
	 * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true
	 *         返回正确信息 false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
	 */
	private static String verifyResponse(String notify_id) {
		// 获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求

		String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner
				+ "&notify_id=" + notify_id;

		return checkUrl(veryfy_url);
	}

	/**
	 * 验证消息是否是支付宝发出的合法消息
	 * 
	 * @param params
	 *            通知返回来的参数数组
	 * @return 验证结果
	 */
	public static boolean verify(Map<String, String> params) {

		// 判断responsetTxt是否为true，isSign是否为true
		// responsetTxt的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
		// isSign不是true，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
		String responseTxt = "true";
		if (params.get("notify_id") != null) {
			String notify_id = params.get("notify_id");
			responseTxt = verifyResponse(notify_id);
		}
		String sign = "";
		if (params.get("sign") != null) {
			sign = params.get("sign");
		}
		boolean isSign = getSignVeryfy(params, sign);

		// 写日志记录（若要调试，请取消下面两行注释）
		// String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign +
		// "\n 返回回来的参数：" + AlipayCore.createLinkString(params);
		// AlipayCore.logResult(sWord);

		if (isSign && responseTxt.equals("true")) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 用于防钓鱼，调用接口query_timestamp来获取时间戳的处理函数 注意：远程解析XML出错，与服务器是否支持SSL等配置有关
	 * 
	 * @return 时间戳字符串
	 * @throws IOException
	 * @throws DocumentException
	 * @throws MalformedURLException
	 */
	public static String query_timestamp() throws MalformedURLException,
			DocumentException, IOException {

		// 构造访问query_timestamp接口的URL串
		String strUrl = ALIPAY_GATEWAY_NEW + "service=query_timestamp&partner="
				+ partner + "&_input_charset" + input_charset;
		StringBuffer result = new StringBuffer();

		SAXReader reader = new SAXReader();
		Document doc = reader.read(new URL(strUrl).openStream());

		List<Node> nodeList = doc.selectNodes("//alipay/*");

		for (Node node : nodeList) {
			// 截取部分不需要解析的信息
			if (node.getName().equals("is_success")
					&& node.getText().equals("T")) {
				// 判断是否有成功标示
				List<Node> nodeList1 = doc
						.selectNodes("//response/timestamp/*");
				for (Node node1 : nodeList1) {
					result.append(node1.getText());
				}
			}
		}
		return result.toString();
	}

	/**
	 * 除去数组中的空值和签名参数
	 * 
	 * @param sArray
	 *            签名参数组
	 * @return 去掉空值与签名参数后的新签名参数组
	 */
	public static Map<String, String> paraFilter(Map<String, String> sArray) {

		Map<String, String> result = new HashMap<String, String>();

		if (sArray == null || sArray.size() <= 0) {
			return result;
		}

		for (String key : sArray.keySet()) {
			String value = sArray.get(key);
			if (value == null || value.equals("")
					|| key.equalsIgnoreCase("sign")
					|| key.equalsIgnoreCase("sign_type")) {
				continue;
			}
			result.put(key, value);
		}
		return result;
	}

	/**
	 * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * 
	 * @param params
	 *            需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {

		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		String prestr = "";

		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);

			if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
				prestr = prestr + key + "=" + value;
			} else {
				prestr = prestr + key + "=" + value + "&";
			}
		}

		return prestr;
	}

	/**
	 * 签名字符串
	 * 
	 * @param text
	 *            需要签名的字符串
	 * @param key
	 *            密钥
	 * @param input_charset
	 *            编码格式
	 * @return 签名结果
	 */
	public static String sign(String text, String key, String input_charset) {
		text = text + key;
		return DigestUtils.md5Hex(getContentBytes(text, input_charset));
	}

	/**
	 * @param content
	 * @param charset
	 * @return
	 * @throws SignatureException
	 * @throws UnsupportedEncodingException
	 */
	private static byte[] getContentBytes(String content, String charset) {
		if (charset == null || "".equals(charset)) {
			return content.getBytes();
		}
		try {
			return content.getBytes(charset);
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:"
					+ charset);
		}
	}

	/**
	 * 生成签名结果
	 * 
	 * @param sPara
	 *            要签名的数组
	 * @return 签名结果字符串
	 */
	public static String buildRequestMysign(Map<String, String> sPara) {
		String prestr = createLinkString(sPara); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
		String mysign = "";
		if (sign_type.equals("MD5")) {
			mysign = sign(prestr, key, input_charset);
		}
		return mysign;
	}

	/**
	 * 生成要请求给支付宝的参数数组
	 * 
	 * @param sParaTemp
	 *            请求前的参数数组
	 * @return 要请求的参数数组
	 */
	private static Map<String, String> buildRequestPara(
			Map<String, String> sParaTemp) {
		// 除去数组中的空值和签名参数
		Map<String, String> sPara = paraFilter(sParaTemp);
		// 生成签名结果
		String mysign = buildRequestMysign(sPara);

		// 签名结果与签名方式加入请求提交参数组中
		sPara.put("sign", mysign);
		sPara.put("sign_type", sign_type);

		return sPara;
	}

	/**
	 * 建立请求，以表单HTML形式构造（默认）
	 * 
	 * @param sParaTemp
	 *            请求参数数组
	 * @param strMethod
	 *            提交方式。两个值可选：post、get
	 * @param strButtonName
	 *            确认按钮显示文字
	 * @return 提交表单HTML文本
	 */
	public static String buildRequest(Map<String, String> sParaTemp,
			String strMethod, String strButtonName) {
		// 待请求参数数组
		Map<String, String> sPara = buildRequestPara(sParaTemp);
		List<String> keys = new ArrayList<String>(sPara.keySet());

		StringBuffer sbHtml = new StringBuffer();

		sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\""
				+ ALIPAY_GATEWAY_NEW
				+ "_input_charset="
				+ input_charset
				+ "\" method=\"" + strMethod + "\">");

		for (int i = 0; i < keys.size(); i++) {
			String name = (String) keys.get(i);
			String value = (String) sPara.get(name);

			sbHtml.append("<input type=\"hidden\" name=\"" + name
					+ "\" value=\"" + value + "\"/>");
		}

		// submit按钮控件请不要含有name属性
		sbHtml.append("<input type=\"submit\" value=\"" + strButtonName
				+ "\" style=\"display:none;\"></form>");
		sbHtml.append("<script>document.forms['alipaysubmit'].submit();</script>");

		return sbHtml.toString();
	}

}
